﻿#region << 版 本 注 释 >>

/*----------------------------------------------------------------
* 类 名 称 ：StateMachine
* 类 描 述 ：
* 作    者 ：henrychang
* 创建时间 ：2019/1/27 23:52:59
*******************************************************************
* Copyright @ henrychang 2019. All rights reserved.
*******************************************************************
//----------------------------------------------------------------*/

#endregion << 版 本 注 释 >>

using System;
using System.Collections.Generic;
using TouchFish.ToolClass;

namespace TouchFish.StateMachine
{
    public sealed class StateMachine<T> : StateMachineBase, IStateMachine<T> where T : class
    {
        private readonly T _owner;

        private readonly Dictionary<string, State<T>> _states;

        private State<T> _currentState;
        private bool _isDestroyed;

        /// <summary>
        /// 初始化状态机的新实例
        /// </summary>
        /// <param name="name">状态机名称</param>
        /// <param name="owner">状态机持有者</param>
        /// <param name="states">状态机状态集合</param>
        public StateMachine(string name, T owner, params State<T>[] states)
            : base(name)
        {
            if (owner == null)
            {
                throw new Exception("StateMachine owner is invalid.");
            }

            if (states == null || states.Length < 1)
            {
                throw new Exception("StateMachine states is invalid.");
            }

            _owner = owner;
            _states = new Dictionary<string, State<T>>();

            foreach (State<T> state in states)
            {
                if (state == null)
                {
                    throw new Exception("StateMachine states is invalid.");
                }

                string stateName = state.GetType().FullName;
                if (_states.ContainsKey(stateName))
                {
                    throw new Exception(StringUtil.Format("StateMachine '{0}' state '{1}' is already exist.", StringUtil.GetFullName<T>(name), stateName));
                }

                _states.Add(stateName, state);
                state.OnInit(this);
            }

            _currentState = null;
            _isDestroyed = false;
        }

        /// <summary>
        /// 获取状态机持有者
        /// </summary>
        public T Owner
        {
            get
            {
                return _owner;
            }
        }

        /// <summary>
        /// 获取状态机持有者类型
        /// </summary>
        public override Type OwnerType
        {
            get
            {
                return typeof(T);
            }
        }

        /// <summary>
        /// 获取状态机中状态的数量
        /// </summary>
        public override int StateCount
        {
            get
            {
                return _states.Count;
            }
        }

        /// <summary>
        /// 获取状态机是否正在运行
        /// </summary>
        public override bool IsRunning
        {
            get
            {
                return _currentState != null;
            }
        }

        /// <summary>
        /// 获取状态机是否被销毁
        /// </summary>
        public override bool IsDestroyed
        {
            get
            {
                return _isDestroyed;
            }
        }

        /// <summary>
        /// 获取当前状态机状态
        /// </summary>
        public State<T> CurrentState
        {
            get
            {
                return _currentState;
            }
        }

        /// <summary>
        /// 获取当前状态机状态名称
        /// </summary>
        public override string CurrentStateName
        {
            get
            {
                return _currentState != null ? _currentState.GetType().FullName : null;
            }
        }

        /// <summary>
        /// 开始状态机
        /// </summary>
        /// <typeparam name="TState">要开始的状态机状态类型</typeparam>
        public void Start<TState>() where TState : State<T>
        {
            if (IsRunning)
            {
                throw new Exception("StateMachine is running, can not start again.");
            }

            State<T> state = GetState<TState>();
            if (state == null)
            {
                throw new Exception(StringUtil.Format("StateMachine '{0}' can not start state '{1}' which is not exist.", StringUtil.GetFullName<T>(Name), typeof(TState).FullName));
            }

            _currentState = state;
            _currentState.OnEnter(this);
        }

        /// <summary>
        /// 开始状态机
        /// </summary>
        /// <param name="stateType">要开始的状态机状态类型</param>
        public void Start(Type stateType)
        {
            if (IsRunning)
            {
                throw new Exception("StateMachine is running, can not start again.");
            }

            if (stateType == null)
            {
                throw new Exception("State type is invalid.");
            }

            if (!typeof(State<T>).IsAssignableFrom(stateType))
            {
                throw new Exception(StringUtil.Format("State type '{0}' is invalid.", stateType.FullName));
            }

            State<T> state = GetState(stateType);
            if (state == null)
            {
                throw new Exception(StringUtil.Format("StateMachine '{0}' can not start state '{1}' which is not exist.", StringUtil.GetFullName<T>(Name), stateType.FullName));
            }

            _currentState = state;
            _currentState.OnEnter(this);
        }

        /// <summary>
        /// 是否存在状态机状态
        /// </summary>
        /// <typeparam name="TState">要检查的状态机状态类型</typeparam>
        /// <returns>是否存在状态机状态</returns>
        public bool HasState<TState>() where TState : State<T>
        {
            return _states.ContainsKey(typeof(TState).FullName);
        }

        /// <summary>
        /// 是否存在状态机状态
        /// </summary>
        /// <param name="stateType">要检查的状态机状态类型</param>
        /// <returns>是否存在状态机状态</returns>
        public bool HasState(Type stateType)
        {
            if (stateType == null)
            {
                throw new Exception("State type is invalid.");
            }

            if (!typeof(State<T>).IsAssignableFrom(stateType))
            {
                throw new Exception(StringUtil.Format("State type '{0}' is invalid.", stateType.FullName));
            }

            return _states.ContainsKey(stateType.FullName);
        }

        /// <summary>
        /// 获取状态机状态
        /// </summary>
        /// <typeparam name="TState">要获取的状态机状态类型</typeparam>
        /// <returns>要获取的状态机状态。</returns>
        public TState GetState<TState>() where TState : State<T>
        {
            State<T> state = null;
            if (_states.TryGetValue(typeof(TState).FullName, out state))
            {
                return (TState)state;
            }

            return null;
        }

        /// <summary>
        /// 获取状态机状态
        /// </summary>
        /// <param name="stateType">要获取的状态机状态类型</param>
        /// <returns>要获取的状态机状态。</returns>
        public State<T> GetState(Type stateType)
        {
            if (stateType == null)
            {
                throw new Exception("State type is invalid.");
            }

            if (!typeof(State<T>).IsAssignableFrom(stateType))
            {
                throw new Exception(StringUtil.Format("State type '{0}' is invalid.", stateType.FullName));
            }

            State<T> state = null;
            if (_states.TryGetValue(stateType.FullName, out state))
            {
                return state;
            }

            return null;
        }

        /// <summary>
        /// 获取状态机的所有状态
        /// </summary>
        /// <returns>状态机的所有状态</returns>
        public State<T>[] GetAllStates()
        {
            int index = 0;
            State<T>[] results = new State<T>[_states.Count];
            foreach (KeyValuePair<string, State<T>> state in _states)
            {
                results[index++] = state.Value;
            }

            return results;
        }

        /// <summary>
        /// 获取状态机的所有状态
        /// </summary>
        /// <param name="results">状态机的所有状态</param>
        public void GetAllStates(List<State<T>> results)
        {
            if (results == null)
            {
                throw new Exception("Results is invalid.");
            }

            results.Clear();
            foreach (KeyValuePair<string, State<T>> state in _states)
            {
                results.Add(state.Value);
            }
        }

        /// <summary>
        /// 抛出状态机事件
        /// </summary>
        /// <param name="sender">事件源</param>
        /// <param name="eventId">事件编号</param>
        public void FireEvent(int eventId, params object[] param)
        {
            if (_currentState == null)
            {
                throw new Exception("Current state is invalid.");
            }

            _currentState.OnEvent(eventId, param);
        }

        /// <summary>
        /// 有限状态机轮询
        /// </summary>
        internal override void Update()
        {
            if (_currentState == null)
            {
                return;
            }

            _currentState.OnUpdate(this);
        }

        /// <summary>
        /// 关闭并清理状态机
        /// </summary>
        internal override void Shutdown()
        {
            if (_currentState != null)
            {
                _currentState.OnLeave(this);
                _currentState = null;
            }

            foreach (KeyValuePair<string, State<T>> state in _states)
            {
                state.Value.OnDestroy(this);
            }

            _states.Clear();

            _isDestroyed = true;
        }

        /// <summary>
        /// 切换当前状态机状态
        /// </summary>
        /// <typeparam name="TState">要切换到的状态机状态类型</typeparam>
        internal void ChangeState<TState>() where TState : State<T>
        {
            ChangeState(typeof(TState));
        }

        /// <summary>
        /// 切换当前状态机状态
        /// </summary>
        /// <param name="stateType">要切换到的状态机状态类型</param>
        internal void ChangeState(Type stateType)
        {
            if (_currentState == null)
            {
                throw new Exception("Current state is invalid.");
            }

            State<T> state = GetState(stateType);
            if (state == null)
            {
                throw new Exception(StringUtil.Format("StateMachine '{0}' can not change state to '{1}' which is not exist.", StringUtil.GetFullName<T>(Name), stateType.FullName));
            }

            _currentState.OnLeave(this);
            _currentState = state;
            _currentState.OnEnter(this);
        }
    }
}